/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ill_be_bach.model;

import java.io.Serializable;
import java.util.ArrayList;

/**
 *
 * @author Abhi
 */
public class Song implements Serializable {

    private ArrayList<Chord>[] chordsByVoice = new ArrayList[8];
    private int[] currentChordPositionByVoice = new int[MAX_VOICES];
    private Chord lastEnteredChord = null ;
    private boolean currentChordMode = false ;

    private static int MAX_VOICES = 8 ;

    // song properties
    private String title = "";
    private Tempo tempo = Tempo.Allegro;
    private ArrayList<Integer> trebleVoices = new ArrayList<Integer>() ;
    private ArrayList<Integer> bassVoices = new ArrayList<Integer>() ;
    private KeySignature keySignature ;

    public Song() {
        for (int i = 0 ; i < MAX_VOICES ; i++) {
            chordsByVoice[i] = new ArrayList<Chord>();
            currentChordPositionByVoice[i] = 0;
        }
    }

    public ArrayList<Chord> getChords(int voice) {
        return chordsByVoice[voice];
    }

    public void removeChord(int voice) {
        if (currentChordPositionByVoice[voice] != 0) {
            int chordPosition = --currentChordPositionByVoice[voice] ;
            ArrayList<Chord> chords = getChords(voice);
            chords.remove(chordPosition);
        }
    }

    public Chord getLastEnteredChord() {
        return lastEnteredChord ;
    }

    public void addChord(Chord chord, int voice) {
        for (Note note : chord.notes)
            adjustNoteForKeySignature(note);
        getChords(voice).add(currentChordPositionByVoice[voice]++, chord);
        lastEnteredChord = chord ;
    }

    public Chord getCurrentChord(int voice) {
        int currentChordPosition = currentChordPositionByVoice[voice];
        ArrayList<Chord> chords = getChords(voice);
        return currentChordPosition == chords.size()? null: chords.get(currentChordPosition);
    }

    public void setCurrentChord(int voice, Chord chordToSet) {
        ArrayList<Chord> chords = getChords(voice);
        for (int i = 0 ; i < chords.size() ; i++ ) {
            if (chordToSet == chords.get(i)) {
                currentChordPositionByVoice[voice] = i ;
                break ;
            }
        }
    }

    public void rewind(int voice) {
        if (currentChordPositionByVoice[voice] != 0)
            currentChordPositionByVoice[voice]-- ;
    }

    public void forward(int voice) {
        if (currentChordPositionByVoice[voice] < getChords(voice).size())
            currentChordPositionByVoice[voice]++ ;
    }

    public void clear() {
        for (int i = 0 ; i < MAX_VOICES ; i++) {
            getChords(i).clear();
            currentChordPositionByVoice[i] = 0 ;
        }
    }

    public String getTitle() { return title ; }

    public Tempo getTempo() { return tempo ; }

    public ArrayList<Integer> getTrebleVoices() { return trebleVoices ; }

    public ArrayList<Integer> getBassVoices() { return bassVoices ; }

    public KeySignature getKeySignature() { return keySignature ; }

    public void setTempo(Tempo tempo) {this.tempo = tempo ; }

    public void setSongProperties(String title, Tempo tempo, ArrayList<Integer> trebleVoices,
            ArrayList<Integer> bassVoices, KeySignature keySignature) {
        this.title = title ;
        this.tempo = tempo ;
        this.trebleVoices = trebleVoices ;
        this.bassVoices = bassVoices ;
        KeySignature oldKeySignature = this.keySignature;
        this.keySignature = keySignature ;
        for (ArrayList<Chord> chords : this.chordsByVoice) {
            for (Chord chord : chords) {
                for (Note note : chord.notes) {
                    revertAdjustNoteForKeySignature(note, oldKeySignature);
                    this.adjustNoteForKeySignature(note);
                }
            }
        }
    }

    public static int getMaxVoices() {
        return MAX_VOICES ;
    }

    private static void revertAdjustNoteForKeySignature(Note note, KeySignature keySignature) {
        if (keySignature.getAccidentalKeys().contains(note.key)) {
            if (note.accidental == Accidental.None) {
                if (keySignature.isFlatSignature())
                    note.accidental = Accidental.Flat;
                else
                    note.accidental = Accidental.Sharp;
            }
            else if (note.accidental == Accidental.Natural)
                note.accidental = Accidental.None;
        }
        if (note.accidental == Accidental.Flat) {
            if (note.key == Key.D)
                note.key = Key.C ;
            if (note.key == Key.E)
                note.key = Key.D ;
            if (note.key == Key.G)
                note.key = Key.F ;
            if (note.key == Key.A)
                note.key = Key.G ;
            if (note.key == Key.B)
                note.key = Key.A ;
            note.accidental = Accidental.Sharp;
        }
    }

    private void adjustNoteForKeySignature(Note note) {
        if (keySignature == null)
            return ;
        if (keySignature.getAccidentalKeys().contains(note.key)){
            if (note.accidental == Accidental.None)
                note.accidental = Accidental.Natural;
            else if (note.accidental == Accidental.Flat
                    || note.accidental == Accidental.Sharp)
                note.accidental = Accidental.None;
        }
        if (note.accidental == Accidental.Sharp && keySignature.isFlatSignature()) {
            if (note.key == Key.C)
                note.key = Key.D ;
            else if (note.key == Key.D)
                note.key = Key.E;
            else if (note.key == Key.F)
                note.key = Key.G ;
            else if (note.key == Key.G)
                note.key = Key.A ;
            else if (note.key == Key.A)
                note.key = Key.B ;
            note.accidental = Accidental.Flat;
        }
    }
}
